/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.semi.lex;

import com.semi.lex.Token;
import com.semi.util.FormatException;
import com.semi.util.StringUtil;
import java.io.BufferedReader;
import java.io.IOException;

/**
 *
 * @author liaoyq
 */
public class SemiLexer {
    private char peek = ' ';
    private char peek2;
    private boolean unget = false;
    private BufferedReader reader = null;
    private boolean eof = false;
    private int offset =0;
    public int Offset(){ return offset; }
    public SemiLexer(BufferedReader r){
        reader = r;
        offset = 0 ;
        eof = false;
        unget = false;
    }
    public void set(BufferedReader r){
         reader = r;
         peek = ' ';
         eof = false;
         offset = 0;
         unget = false;
    }
    protected void unget(){
        if(eof) return;
        peek2 = peek;
        unget =true;
    }
    protected  int readch() throws IOException {
        if( unget ){
            unget = false;
            peek = peek2;
            return 1;
        }
        if( eof ) return 0;
        peek = (char)reader.read();
        offset++;
        if( (int)peek == 0xffff) eof = true;
        return 1;
    }
    private static boolean isSeparator(char ch){
        if( Character.isSpaceChar(ch)) return true;
        return ch=='='|| ch=='>'||ch=='<'||ch=='!'||ch=='+'||ch=='-'||ch=='/';
    }
    private String readString() throws IOException, FormatException{
        StringBuffer sb = new StringBuffer();
        while(readch()>0){
            if( peek=='"'){
                return sb.toString();
            }
            if( peek=='\\'){
                readch();
                switch(peek){
                    case 'n':sb.append('\n');break;
                    case 't':sb.append('\t');break;
                    default:sb.append(peek);break;
                }
                continue;
            }
            sb.append(peek);
        }
        throw new FormatException("unexpect end when read string");
    }
    public Token scan() throws IOException, FormatException {
        while( readch()>0 ){
            if( Character.isSpaceChar(peek) ) continue;
            else break;
        }
        if( eof ) return null;
        if( peek=='\n') return null;
            switch(peek){
                case '=': readch();
                    if( peek=='=' ) return Token.eq;
                    unget();
                    return new Token('=',"=");
                case '>':
                    readch();
                    if( peek =='=') return Token.ge;
                    unget();
                    return new Token('>',">");
                case '<':
                    readch();
                    if( peek=='=') return Token.le;
                    unget();
                    return new Token('<',"<");                    
                case '!': readch();
                    if( peek=='=') return Token.ne;
                    unget();
                    return new Token('!',"!");
                case '.': readch();
                    if( peek=='.') return new Token(Token.ID,"..");
                    unget();
                    return new Token('.',".");
                case '"':
                    return new Token(Token.STRING,readString());
                default:
                   break;
            }
            if(Character.isDigit(peek)) {
                String tmp ="";
                int dot=0,colon=0,k=0;
                do{
                    if( Character.isDigit(peek) ) tmp+=peek;
                    else if( peek=='.'){ 
                        if( k>0|| colon>0  ) throw new FormatException("error ip format");
                        dot++;tmp+=peek;
                    }
                    else if( peek==':'){
                        if( k>0|| dot>0  ) throw new FormatException("error ip format");
                        colon++; tmp +=peek;
                    }
                    else if( peek=='/'){
                        k++;
                        if( k>1 )  throw new FormatException("error host format, too many '/'");
                        tmp +=peek;
                    }
                    else break;
                }while(readch()>0);
                if( eof || isSeparator(peek) ){
                    unget();
                    if( dot==0 && colon==0 ) return new IntToken(tmp);
                    if( dot==1 && colon==0 ) return new FloatToken(tmp);
                    if( colon==0 && dot==3 ) return new Token(Token.IPV4,tmp);                            
                     if( colon==5 && dot==0 ) return new Token(Token.IPV6,tmp);
                    throw new FormatException("error ip format");
                }
                throw new FormatException("error number");
            }
            if( Character.isLetter(peek)|| peek=='_'|| peek==':'||peek=='_'||peek=='$'||peek=='#'){
                StringBuffer sb = new StringBuffer();
                do{
                    if( Character.isLetterOrDigit(peek))
                    sb.append(peek);
                    else break;
                }while(readch()>0);
                return new Token(Token.ID,sb.toString());
            }
            throw new FormatException("unexpect char '"+peek+"'");
                
    }
}
